#region (c) 2014 Nguyễn, M. Hải - All rights reserved
/*
BLADE VIEW ENGINE

The MIT License (MIT)
Copyright (c) 2014 Nguyễn, M. Hải

Permission is hereby granted, free of charge, to any person obtaining a copy of this 
software and associated documentation files (the "Software"), to deal in the Software 
without restriction, including without limitation the rights to use, copy, modify, merge
, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 
persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
 substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
DEALINGS IN THE SOFTWARE.
*/
#endregion

#region Record of Change
//             							CHANGE HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | DEVELOPER  | DESCRIPTION                                              |
// -------------------------------------------------------------------------------------
// | 27-Apr-14 | HaiNM      | First creation.                                          |
// -------------------------------------------------------------------------------------
//
#endregion

#region Record of Audit
//             							REVIEW HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | REVIEWER   | NOTES                                                    |
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
//
#endregion

#region Using directives
using System;
using System.Text;

using Thn;
using Thn.Collections;
using Thn.IO;
#endregion

namespace Thn.Web.Blade
{
    /// <summary>
    /// Responsible for generating C# source-code from a text template.
    /// </summary>
#if DEBUG || TESTING
    public
#else
    public
#endif
 class BladeCodeGenerator
    {
        #region Fields
        /// <summary>
        /// Storing the final results.
        /// </summary>
        StringBuilder mBuilder;
        BladeParser parser;

#if DEBUG || TESTING
        bool Trace = true;
#endif

        #endregion

        #region Properties

        #region Referenced Namespaces
        private List<string> mReferencedNamespaces;
        /// <summary>
        /// Gets or sets list of referenced namespaces.
        /// </summary>
        public List<string> ReferencedNamespaces
        {
            get { return mReferencedNamespaces; }
            set { mReferencedNamespaces = value; }
        }
        #endregion

        #region Namespace
        private string mNamespace = "___BLADE___";
        /// <summary>
        /// Gets or sets namespace of generated class.
        /// </summary>
        public string Namespace
        {
            get { return mNamespace; }
            set { mNamespace = value; }
        }
        #endregion

        #region Class Name
        private string mClassName = "MyClass";
        /// <summary>
        /// Gets or sets name of generated class.
        /// </summary>
        public string ClassName
        {
            get { return mClassName; }
            set { mClassName = value; }
        }
        #endregion

        #region Parent Name
        private string mParentName = "BladeViewTemplate";
        /// <summary>
        /// Gets or sets name of parent type.
        /// </summary>
        public string ParentName
        {
            get { return mParentName; }
            set { mParentName = value; }
        }
        #endregion

        #region Main Method Signature
        private string mMainMethodSignature = "public override void Execute()";
        /// <summary>
        /// Gets or sets signature of the method to inject parsed content into.
        /// </summary>
        public string MainMethodSignature
        {
            get { return mMainMethodSignature; }
            set { mMainMethodSignature = value; }
        }
        #endregion
        
        #endregion

        #region Generate
        /// <summary>
        /// Generate source-code from text template.
        /// </summary>
        /// <param name="template">The source text template to generate codes from.</param>
        public string Generate(string template)
        {
            var reader = new CharReader(template);
            return Generate(reader);
        }
        /// <summary>
        /// Generate source-code from text template.
        /// </summary>
        /// <param name="reader">The reader responsible for reading characters.</param>
        public string Generate(ICharReader reader)
        {
            mBuilder = new StringBuilder();

            AppendHeaders();

            Parse(reader);

            AppendFooters();

            return mBuilder.ToString();
        }
        #endregion

        #region Append Headers
        void AppendHeaders()
        {
            //notices
            mBuilder.Append(@"//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by BLADE View Engine.
//     Version: 6.5
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------");
            mBuilder.AppendLine();

            //append usings
            foreach (var item in mReferencedNamespaces)
            {
                mBuilder.AppendLine(string.Format("using {0};", item));
            }
            mBuilder.AppendLine();

            //namespace
            mBuilder.AppendLine("namespace " + mNamespace);
            mBuilder.AppendLine("{");

            //class name
            mBuilder.AppendLine("\tpublic class " + mClassName + " : " + mParentName);
            mBuilder.AppendLine("\t{");

            //constructor
            mBuilder.AppendLine("\t\tpublic " + mClassName + "(){}");

            //execute
            //mBuilder.AppendLine("\t\tpublic override void Execute(){");
            mBuilder.AppendLine("\t\t" + mMainMethodSignature);
            mBuilder.AppendLine("\t\t{");
        }
        #endregion

        #region Append Footers
        void AppendFooters()
        {
            mBuilder.AppendLine();

            //execute
            mBuilder.AppendLine("\t\t}");

            //class
            mBuilder.AppendLine("\t}");

            //namespace
            mBuilder.AppendLine("}");
        }
        #endregion

        #region Parse
        void Parse(ICharReader reader)
        {
            //initialize if necessary
            if (parser == null)
            {
                parser = new BladeParser();
                parser.FoundText += parser_FoundText;
                parser.FoundCodeBlock += parser_FoundCodeBlock;
                parser.FoundInlineCode += parser_FoundInlineCode;
            }

            parser.Parse(reader);
        }
        #endregion

        #region Parser - Text
        void parser_FoundText(string text)
        {
            //string literals = text.Trim();
            //literals = literals.Trim();

            string literals = text;

            //escape literals
            literals = literals.Replace("\\", "\\\\");
            literals = literals.Replace("\"", "\\\"");
            literals = literals.Replace("\r", "\\r");
            literals = literals.Replace("\n", "\\n");
            //Note by Hai: the above can be optimized using TextEscaper

            mBuilder.Append("\t\t\t");
            mBuilder.Append("WriteLiteral(\"");
            mBuilder.Append(literals);
            mBuilder.AppendLine("\");");
        }
        #endregion

        #region Parser - Inline code
        void parser_FoundInlineCode(string code)
        {
            mBuilder.Append("\t\t\t");
            mBuilder.Append("Write(");
            mBuilder.Append(code);
            mBuilder.AppendLine(");");
        }
        #endregion

        #region Parser - Code block
        void parser_FoundCodeBlock(string code)
        {
            mBuilder.Append(code);
        }
        #endregion

        #region Process Code
        //void ProcessCode(string codes)
        //{
        //    mBuilder.Append(codes);
        //}
        #endregion

        #region Constructors
        /// <summary>
        /// Create a new instance.
        /// </summary>
        public BladeCodeGenerator()
            : base()
        {
            mReferencedNamespaces = new List<string>();
            mReferencedNamespaces.Add("System");            
            
#if OPENSOURCE
            mReferencedNamespaces.Add("System.IO");
            mReferencedNamespaces.Add("System.Collections");
            mReferencedNamespaces.Add("System.Collections.Generic");
#else
            mReferencedNamespaces.Add("System.IO");
            mReferencedNamespaces.Add("Thn.Collections");
#endif

            mReferencedNamespaces.Add("Thn");
            mReferencedNamespaces.Add("Thn.Web");
            mReferencedNamespaces.Add("Thn.Web.Mvc");
            mReferencedNamespaces.Add("Thn.Web.Blade");
        }
        #endregion
    }
}
